<!DOCTYPE html>


<html theme="dark" showBanner="true" hasBanner="false" > 
<link href="https://cdn.staticfile.org/font-awesome/6.4.2/css/fontawesome.min.css" rel="stylesheet">
<link href="https://cdn.staticfile.org/font-awesome/6.4.2/css/brands.min.css" rel="stylesheet">
<link href="https://cdn.staticfile.org/font-awesome/6.4.2/css/solid.min.css" rel="stylesheet">
<script src="/hexo-blog/js/color.global.min.js" ></script>
<script src="/hexo-blog/js/load-settings.js" ></script>
<head>
  <meta charset="utf-8">
  
  
  

  
  <title>前端脚手架：从入门到进阶——Create React App源码解析（二） | bressanone</title>
  <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">

  <link rel="preload" href="/hexo-blog/css/fonts/Roboto-Regular.ttf" as="font" type="font/ttf" crossorigin="anonymous">
  <link rel="preload" href="/hexo-blog/css/fonts/Roboto-Bold.ttf" as="font" type="font/ttf" crossorigin="anonymous">

  <meta name="description" content="这是我参与11月更文挑战的第 5 天，活动详情查看：2021最后一次更文挑战 序在开始源码解析前我们先来认识一下cra。你可能思考过这些问题： 为什么用npx而不是全局安装？npm 5.2.0之后提供了npx命令，当调用npx &lt;command&gt;，而你的$PATH没有&lt;command&gt; 时，将会自动安装这个名字的包，并执行它。执行完后，这个安装下来的包会被清理掉。 使用np">
<meta property="og:type" content="article">
<meta property="og:title" content="前端脚手架：从入门到进阶——Create React App源码解析（二）">
<meta property="og:url" content="http://example.com/2021/11/05/%E5%89%8D%E7%AB%AF%E8%84%9A%E6%89%8B%E6%9E%B6%EF%BC%9A%E4%BB%8E%E5%85%A5%E9%97%A8%E5%88%B0%E8%BF%9B%E9%98%B6%E2%80%94%E2%80%94Create%20React%20App%E6%BA%90%E7%A0%81%E8%A7%A3%E6%9E%90%EF%BC%88%E4%BA%8C%EF%BC%89/index.html">
<meta property="og:site_name" content="bressanone">
<meta property="og:description" content="这是我参与11月更文挑战的第 5 天，活动详情查看：2021最后一次更文挑战 序在开始源码解析前我们先来认识一下cra。你可能思考过这些问题： 为什么用npx而不是全局安装？npm 5.2.0之后提供了npx命令，当调用npx &lt;command&gt;，而你的$PATH没有&lt;command&gt; 时，将会自动安装这个名字的包，并执行它。执行完后，这个安装下来的包会被清理掉。 使用np">
<meta property="og:locale" content="zh_CN">
<meta property="og:image" content="http://example.com/2021/11/05/imgs/026bf9f862e8483f86ecaaf73ec4cb44.png">
<meta property="og:image" content="http://example.com/2021/11/05/imgs/fb244a2e456a42569d5f8887d5c3ca77.png">
<meta property="og:image" content="http://example.com/2021/11/05/imgs/2aec0f4ee07846c0b7eabf3672691b57.png">
<meta property="og:image" content="http://example.com/2021/11/05/imgs/578e5e1e1c6a4f34a9d30ab36f34dcce.png">
<meta property="og:image" content="http://example.com/2021/11/05/imgs/702ab02646dc49fb893867edb9fd0142.png">
<meta property="article:published_time" content="2021-11-04T16:00:00.000Z">
<meta property="article:modified_time" content="2024-04-04T15:27:03.289Z">
<meta property="article:author" content="Zachary">
<meta property="article:tag" content="前端">
<meta property="article:tag" content="React.js">
<meta name="twitter:card" content="summary">
<meta name="twitter:image" content="http://example.com/2021/11/05/imgs/026bf9f862e8483f86ecaaf73ec4cb44.png">
  
  
    <link rel="icon" media="(prefers-color-scheme: light)" href="/hexo-blog/images/favicon-light-32.png" sizes="32x32">
    <link rel="icon" media="(prefers-color-scheme: light)" href="/hexo-blog/images/favicon-light-128.png" sizes="128x128">
    <link rel="icon" media="(prefers-color-scheme: light)" href="/hexo-blog/images/favicon-light-180.png" sizes="180x180">
    <link rel="icon" media="(prefers-color-scheme: light)" href="/hexo-blog/images/favicon-light-192.png" sizes="192x192">
    <link rel="icon" media="(prefers-color-scheme: dark)" href="/hexo-blog/images/favicon-dark-32.png" sizes="32x32">
    <link rel="icon" media="(prefers-color-scheme: dark)" href="/hexo-blog/images/favicon-dark-128.png" sizes="128x128">
    <link rel="icon" media="(prefers-color-scheme: dark)" href="/hexo-blog/images/favicon-dark-180.png" sizes="180x180">
    <link rel="icon" media="(prefers-color-scheme: dark)" href="/hexo-blog/images/favicon-dark-192.png" sizes="192x192">
  
  
<link rel="stylesheet" href="/hexo-blog/css/style.css">

<meta name="generator" content="Hexo 7.1.1"></head>

<body>
  
   
  <div id="main-grid" class="  ">
    <div id="nav" class=""  >
      <navbar id="navbar">
  <nav id="title-nav">
    <a href="/hexo-blog/">
      <div id="vivia-logo">
        <div class="dot"></div>
        <div class="dot"></div>
        <div class="dot"></div>
        <div class="dot"></div>
      </div>
      <div>bressanone </div>
    </a>
  </nav>
  <nav id="main-nav">
    
      <a class="main-nav-link" href="/hexo-blog/">主页</a>
    
      <a class="main-nav-link" href="/hexo-blog/archives">归档</a>
    
      <a class="main-nav-link" href="/hexo-blog/about">关于</a>
    
  </nav>
  <nav id="sub-nav">
    <a id="theme-btn" class="nav-icon">
      <span class="light-mode-icon"><svg xmlns="http://www.w3.org/2000/svg" height="20" viewBox="0 -960 960 960" width="20"><path d="M438.5-829.913v-48q0-17.452 11.963-29.476 11.964-12.024 29.326-12.024 17.363 0 29.537 12.024t12.174 29.476v48q0 17.452-11.963 29.476-11.964 12.024-29.326 12.024-17.363 0-29.537-12.024T438.5-829.913Zm0 747.826v-48q0-17.452 11.963-29.476 11.964-12.024 29.326-12.024 17.363 0 29.537 12.024t12.174 29.476v48q0 17.452-11.963 29.476-11.964 12.024-29.326 12.024-17.363 0-29.537-12.024T438.5-82.087ZM877.913-438.5h-48q-17.452 0-29.476-11.963-12.024-11.964-12.024-29.326 0-17.363 12.024-29.537t29.476-12.174h48q17.452 0 29.476 11.963 12.024 11.964 12.024 29.326 0 17.363-12.024 29.537T877.913-438.5Zm-747.826 0h-48q-17.452 0-29.476-11.963-12.024-11.964-12.024-29.326 0-17.363 12.024-29.537T82.087-521.5h48q17.452 0 29.476 11.963 12.024 11.964 12.024 29.326 0 17.363-12.024 29.537T130.087-438.5Zm660.174-290.87-34.239 32q-12.913 12.674-29.565 12.174-16.653-.5-29.327-13.174-12.674-12.673-12.554-28.826.12-16.152 12.794-28.826l33-35q12.913-12.674 30.454-12.674t30.163 12.847q12.709 12.846 12.328 30.826-.38 17.98-13.054 30.653ZM262.63-203.978l-32 34q-12.913 12.674-30.454 12.674t-30.163-12.847q-12.709-12.846-12.328-30.826.38-17.98 13.054-30.653l33.239-31q12.913-12.674 29.565-12.174 16.653.5 29.327 13.174 12.674 12.673 12.554 28.826-.12 16.152-12.794 28.826Zm466.74 33.239-32-33.239q-12.674-12.913-12.174-29.565.5-16.653 13.174-29.327 12.673-12.674 28.826-13.054 16.152-.38 28.826 12.294l35 33q12.674 12.913 12.674 30.454t-12.847 30.163q-12.846 12.709-30.826 12.328-17.98-.38-30.653-13.054ZM203.978-697.37l-34-33q-12.674-12.913-13.174-29.945-.5-17.033 12.174-29.707t31.326-13.293q18.653-.62 31.326 13.054l32 34.239q11.674 12.913 11.174 29.565-.5 16.653-13.174 29.327-12.673 12.674-28.826 12.554-16.152-.12-28.826-12.794ZM480-240q-100 0-170-70t-70-170q0-100 70-170t170-70q100 0 170 70t70 170q0 100-70 170t-170 70Zm-.247-82q65.703 0 111.475-46.272Q637-414.544 637-480.247t-45.525-111.228Q545.95-637 480.247-637t-111.475 45.525Q323-545.95 323-480.247t45.525 111.975Q414.05-322 479.753-322ZM481-481Z"/></svg></span>
      <span class="dark-mode-icon"><svg xmlns="http://www.w3.org/2000/svg" height="20" viewBox="0 -960 960 960" width="20"><path d="M480.239-116.413q-152.63 0-258.228-105.478Q116.413-327.37 116.413-480q0-130.935 77.739-227.435t206.304-125.043q43.022-9.631 63.87 10.869t3.478 62.805q-8.891 22.043-14.315 44.463-5.424 22.42-5.424 46.689 0 91.694 64.326 155.879 64.325 64.186 156.218 64.186 24.369 0 46.978-4.946 22.609-4.945 44.413-14.076 42.826-17.369 62.967 1.142 20.142 18.511 10.511 61.054Q807.174-280 712.63-198.206q-94.543 81.793-232.391 81.793Zm0-95q79.783 0 143.337-40.217 63.554-40.218 95.793-108.283-15.608 4.044-31.097 5.326-15.49 1.283-31.859.805-123.706-4.066-210.777-90.539-87.071-86.473-91.614-212.092-.24-16.369.923-31.978 1.164-15.609 5.446-30.978-67.826 32.478-108.282 96.152Q211.652-559.543 211.652-480q0 111.929 78.329 190.258 78.329 78.329 190.258 78.329ZM466.13-465.891Z"/></svg></span>
    </a>
    
    <div id="nav-menu-btn" class="nav-icon">
      <svg xmlns="http://www.w3.org/2000/svg" height="20" viewBox="0 -960 960 960" width="20"><path d="M177.37-252.282q-17.453 0-29.477-11.964-12.024-11.963-12.024-29.326t12.024-29.537q12.024-12.174 29.477-12.174h605.26q17.453 0 29.477 11.964 12.024 11.963 12.024 29.326t-12.024 29.537q-12.024 12.174-29.477 12.174H177.37Zm0-186.218q-17.453 0-29.477-11.963-12.024-11.964-12.024-29.326 0-17.363 12.024-29.537T177.37-521.5h605.26q17.453 0 29.477 11.963 12.024 11.964 12.024 29.326 0 17.363-12.024 29.537T782.63-438.5H177.37Zm0-186.217q-17.453 0-29.477-11.964-12.024-11.963-12.024-29.326t12.024-29.537q12.024-12.174 29.477-12.174h605.26q17.453 0 29.477 11.964 12.024 11.963 12.024 29.326t-12.024 29.537q-12.024 12.174-29.477 12.174H177.37Z"/></svg>
    </div>
  </nav>
</navbar>
<div id="nav-dropdown" class="hidden">
  <div id="dropdown-link-list">
    
      <a class="nav-dropdown-link" href="/hexo-blog/">主页</a>
    
      <a class="nav-dropdown-link" href="/hexo-blog/archives">归档</a>
    
      <a class="nav-dropdown-link" href="/hexo-blog/about">关于</a>
    
     
    </div>
</div>
<script>
  let dropdownBtn = document.getElementById("nav-menu-btn");
  let dropdownEle = document.getElementById("nav-dropdown");
  dropdownBtn.onclick = function() {
    dropdownEle.classList.toggle("hidden");
  }
</script>
    </div>
    <div id="sidebar-wrapper">
      <sidebar id="sidebar">
  
    <div class="widget-wrap">
  <div class="info-card">
    <div class="avatar">
      
      <div class="img-dim"></div>
    </div>
    <div class="info">
      <div class="username">Zachary </div>
      <div class="dot"></div>
      <div class="subtitle"> </div>
      <div class="link-list">
        
          <a class="link-btn" target="_blank" rel="noopener" href="https://github.com/zzy1661" title="GitHub"><i class="fa-brands fa-github"></i></a>
         
      </div>  
    </div>
  </div>
</div>

  
  <div class="sticky">
    
      


  <div class="widget-wrap">
    <div class="widget">
      <h3 class="widget-title">分类</h3>
      <div class="category-box">
            <a class="category-link" href="/hexo-blog/categories/%E5%89%8D%E7%AB%AF%E7%AC%94%E8%AE%B0/">
                前端笔记
                <div class="category-count">78</div>
            </a>
        
            <a class="category-link" href="/hexo-blog/categories/%E7%AE%97%E6%B3%95%E6%89%8B%E5%86%8C/">
                算法手册
                <div class="category-count">7</div>
            </a>
        </div>
    </div>
  </div>


    
      
  <div class="widget-wrap">
    <div class="widget">
      <h3 class="widget-title">标签</h3>
      <ul class="widget-tag-list" itemprop="keywords"><li class="widget-tag-list-item"><a class="widget-tag-list-link" href="/hexo-blog/tags/Ant-Design/" rel="tag">Ant Design</a></li><li class="widget-tag-list-item"><a class="widget-tag-list-link" href="/hexo-blog/tags/CSS/" rel="tag">CSS</a></li><li class="widget-tag-list-item"><a class="widget-tag-list-link" href="/hexo-blog/tags/Canvas/" rel="tag">Canvas</a></li><li class="widget-tag-list-item"><a class="widget-tag-list-link" href="/hexo-blog/tags/Docker/" rel="tag">Docker</a></li><li class="widget-tag-list-item"><a class="widget-tag-list-link" href="/hexo-blog/tags/ECMAScript-6/" rel="tag">ECMAScript 6</a></li><li class="widget-tag-list-item"><a class="widget-tag-list-link" href="/hexo-blog/tags/JavaScript/" rel="tag">JavaScript</a></li><li class="widget-tag-list-item"><a class="widget-tag-list-link" href="/hexo-blog/tags/Less/" rel="tag">Less</a></li><li class="widget-tag-list-item"><a class="widget-tag-list-link" href="/hexo-blog/tags/MobX/" rel="tag">MobX</a></li><li class="widget-tag-list-item"><a class="widget-tag-list-link" href="/hexo-blog/tags/MongoDB/" rel="tag">MongoDB</a></li><li class="widget-tag-list-item"><a class="widget-tag-list-link" href="/hexo-blog/tags/NPM/" rel="tag">NPM</a></li><li class="widget-tag-list-item"><a class="widget-tag-list-link" href="/hexo-blog/tags/NestJS/" rel="tag">NestJS</a></li><li class="widget-tag-list-item"><a class="widget-tag-list-link" href="/hexo-blog/tags/Node-js/" rel="tag">Node.js</a></li><li class="widget-tag-list-item"><a class="widget-tag-list-link" href="/hexo-blog/tags/Postman/" rel="tag">Postman</a></li><li class="widget-tag-list-item"><a class="widget-tag-list-link" href="/hexo-blog/tags/React-js/" rel="tag">React.js</a></li><li class="widget-tag-list-item"><a class="widget-tag-list-link" href="/hexo-blog/tags/Redux/" rel="tag">Redux</a></li><li class="widget-tag-list-item"><a class="widget-tag-list-link" href="/hexo-blog/tags/TypeScript/" rel="tag">TypeScript</a></li><li class="widget-tag-list-item"><a class="widget-tag-list-link" href="/hexo-blog/tags/UML/" rel="tag">UML</a></li><li class="widget-tag-list-item"><a class="widget-tag-list-link" href="/hexo-blog/tags/Webpack/" rel="tag">Webpack</a></li><li class="widget-tag-list-item"><a class="widget-tag-list-link" href="/hexo-blog/tags/Yeoman/" rel="tag">Yeoman</a></li><li class="widget-tag-list-item"><a class="widget-tag-list-link" href="/hexo-blog/tags/%E5%89%8D%E7%AB%AF/" rel="tag">前端</a></li><li class="widget-tag-list-item"><a class="widget-tag-list-link" href="/hexo-blog/tags/%E6%9E%B6%E6%9E%84/" rel="tag">架构</a></li><li class="widget-tag-list-item"><a class="widget-tag-list-link" href="/hexo-blog/tags/%E6%AD%A3%E5%88%99%E8%A1%A8%E8%BE%BE%E5%BC%8F/" rel="tag">正则表达式</a></li><li class="widget-tag-list-item"><a class="widget-tag-list-link" href="/hexo-blog/tags/%E6%BA%90%E7%A0%81/" rel="tag">源码</a></li><li class="widget-tag-list-item"><a class="widget-tag-list-link" href="/hexo-blog/tags/%E7%88%AC%E8%99%AB/" rel="tag">爬虫</a></li><li class="widget-tag-list-item"><a class="widget-tag-list-link" href="/hexo-blog/tags/%E7%AE%97%E6%B3%95/" rel="tag">算法</a></li><li class="widget-tag-list-item"><a class="widget-tag-list-link" href="/hexo-blog/tags/%E9%9D%A2%E8%AF%95/" rel="tag">面试</a></li></ul>
    </div>
  </div>


    
  </div>
</sidebar>
    </div>
    <div id="content-body">
       


<article id="post-前端脚手架：从入门到进阶——Create React App源码解析（二）" class="h-entry article article-type-post" itemprop="blogPost" itemscope itemtype="https://schema.org/BlogPosting">
  
    
   
  <div class="article-inner">
    <div class="article-main">
      <header class="article-header">
        
<div class="main-title-bar">
  <div class="main-title-dot"></div>
  
    
      <h1 class="p-name article-title" itemprop="headline name">
        前端脚手架：从入门到进阶——Create React App源码解析（二）
      </h1>
    
  
</div>

        <div class='meta-info-bar'>
          <div class="meta-info">
  <time class="dt-published" datetime="2021-11-04T16:00:00.000Z" itemprop="datePublished">2021-11-05</time>
</div>
          <div class="need-seperator meta-info">
            <div class="meta-cate-flex">
  
  <a class="meta-cate-link" href="/hexo-blog/categories/%E5%89%8D%E7%AB%AF%E7%AC%94%E8%AE%B0/">前端笔记</a>
   
</div>
  
          </div>
          <div class="wordcount need-seperator meta-info">
            4.6k 词 
          </div>
        </div>
        
  <ul class="article-tag-list" itemprop="keywords"><li class="article-tag-list-item"><a class="article-tag-list-link" href="/hexo-blog/tags/React-js/" rel="tag">React.js</a></li><li class="article-tag-list-item"><a class="article-tag-list-link" href="/hexo-blog/tags/%E5%89%8D%E7%AB%AF/" rel="tag">前端</a></li></ul>

      </header>
      <div class="e-content article-entry" itemprop="articleBody">
        
          <p>这是我参与11月更文挑战的第 5 天，活动详情查看：<a target="_blank" rel="noopener" href="https://juejin.cn/post/7023643374569816095/" title="https://juejin.cn/post/7023643374569816095/">2021最后一次更文挑战</a></p>
<h2 id="序"><a href="#序" class="headerlink" title="序"></a>序</h2><p>在开始源码解析前我们先来认识一下cra。你可能思考过这些问题：</p>
<h3 id="为什么用npx而不是全局安装？"><a href="#为什么用npx而不是全局安装？" class="headerlink" title="为什么用npx而不是全局安装？"></a>为什么用npx而不是全局安装？</h3><p>npm 5.2.0之后提供了npx命令，当调用<code>npx &lt;command&gt;</code>，而你的<code>$PATH</code>没有<code>&lt;command&gt;</code> 时，将会自动安装这个名字的包，并执行它。执行完后，这个安装下来的包会被清理掉。 使用npx执行cra，能够保证你执行的脚手架始终是最新的版本。关于npx具体的文档可以<a href="https://link.juejin.cn/?target=url" title="url">参考这里</a><a href="https://blog.npmjs.org%2Fpost%2F162869356040%2Fintroducing-npx-an-npm-package-runner" title="https://blog.npmjs.org/post/162869356040/introducing-npx-an-npm-package-runner">blog.npmjs.org&#x2F;post&#x2F;162869…</a></p>
<blockquote>
<p>This feature is ideal for things like generators, too. Tools like <code>yeoman</code> or <code>create-react-app</code> only ever get called once in a blue moon. By the time you run them again, they’ll already be far out of date, so you end up having to run an install every time you want to use them anyway.</p>
</blockquote>
<p><em>这里的once in a blue moon是一个俚语，类似千载难逢的意思。</em></p>
<h3 id="create-react-app命令是如何提供的？"><a href="#create-react-app命令是如何提供的？" class="headerlink" title="create-react-app命令是如何提供的？"></a>create-react-app命令是如何提供的？</h3><p>package.json中的bin属性指定了一个文件，当这个被全局安装时，这个文件将会被连接到全局的bin中，因此可以在任何地方调用。</p>
<blockquote>
<p>A lot of packages have one or more executable files that they’d like to install into the PATH. npm makes this pretty easy … To use this, supply a <code>bin</code> field in your package.json which is a map of command name to local file name. When this package is installed globally, that file will be linked where global bins go so it is available to run by name. 如果一个包中提供了多个命令，还可以指定<code>directories.bin</code>属性，npm会认为这个目录下的所有文件都是可执行的。</p>
</blockquote>
<h3 id="为什么可以以npm-init-命令执行"><a href="#为什么可以以npm-init-命令执行" class="headerlink" title="为什么可以以npm init 命令执行"></a>为什么可以以npm init 命令执行</h3><blockquote>
<p><code>npm init &lt;initializer&gt;</code> …<code>initializer</code> in this case is an npm package named <code>create-&lt;initializer&gt;</code>, which will be installed by <a href="https://npm.im%2Fnpx" title="https://npm.im/npx"><code>npx</code></a>, and then have its main bin executed – presumably creating or updating <code>package.json</code> and running any other initialization-related operations. <code>npm init xx</code> 其实就相当于<code>npx create-xx</code></p>
</blockquote>
<h3 id="usr-bin-env-node-是什么"><a href="#usr-bin-env-node-是什么" class="headerlink" title="#!&#x2F;usr&#x2F;bin&#x2F;env node 是什么"></a>#!&#x2F;usr&#x2F;bin&#x2F;env node 是什么</h3><blockquote>
<p>Please make sure that your file(s) referenced in <code>bin</code> starts with <code>#!/usr/bin/env node</code>, otherwise the scripts are started without the node executable! 这个<code>#!</code>叫做Shebang，这一行叫Shebang行。脚本第一行通常是指定解释器，<code>#!/usr/bin/env NAME</code>这个语法的意思是，让 Shell 查找<code>$PATH</code>环境变量里面第一个匹配的<code>NAME</code>。</p>
</blockquote>
<p>而且担心windows不支持Shebang行。如果是由npm安装的包，npm生成的链接其实是这样的： <img src="./../imgs/026bf9f862e8483f86ecaaf73ec4cb44.png" alt="image.png"></p>
<p>这里指定了使用node执行。</p>
<p>get到这些信息后我们就可以按图索骥，跟着package.json中bin属性指定的文件按顺序往下分析了。</p>
<h2 id="create-react-app-index-js"><a href="#create-react-app-index-js" class="headerlink" title="create-react-app&#x2F;index.js"></a>create-react-app&#x2F;index.js</h2><p>我们跟随package.json顺藤摸瓜来到index.js,这个文件很简单，判断了一下node版本，然后就执行了createReactApp.js中的init方法。</p>
<figure class="highlight vbnet"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> currentNodeVersion = process.versions.node;</span><br><span class="line"><span class="keyword">const</span> semver = currentNodeVersion.split(<span class="comment">&#x27;.&#x27;);</span></span><br><span class="line"><span class="keyword">const</span> major = semver[<span class="number">0</span>];</span><br><span class="line"></span><br><span class="line"><span class="keyword">if</span> (major &lt; <span class="number">14</span>) &#123;</span><br><span class="line">  console.<span class="keyword">error</span>(</span><br><span class="line">    <span class="comment">&#x27;You are running Node &#x27; +</span></span><br><span class="line">      currentNodeVersion +</span><br><span class="line">      <span class="comment">&#x27;.\n&#x27; +</span></span><br><span class="line">      <span class="comment">&#x27;Create React App requires Node 14 or higher. \n&#x27; +</span></span><br><span class="line">      <span class="comment">&#x27;Please update your version of Node.&#x27;</span></span><br><span class="line">  );</span><br><span class="line">  process.<span class="keyword">exit</span>(<span class="number">1</span>);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> &#123; init &#125; = require(<span class="comment">&#x27;./createReactApp&#x27;);</span></span><br><span class="line"></span><br><span class="line">init();</span><br><span class="line"></span><br></pre></td></tr></table></figure>

<p>注意这里node版本低于14就退出了，其实早期的版本对node的要求没有这么新，当遇到一个低版本的node时还有降级方案，随着迭代这个降级代码有些多余了。</p>
<h2 id="create-react-app-createReactApp-js"><a href="#create-react-app-createReactApp-js" class="headerlink" title="create-react-app&#x2F;createReactApp.js"></a>create-react-app&#x2F;createReactApp.js</h2><p>这个文件才是主要代码，一共1000多行，我们先来将它删减一下</p>
<p>这个部分可以删掉，program.info来自于用户输入<code>--info</code>,只是打印了一些环境信息；没有projectName时只是进行了log后就退出了，都不影响主流程。 <img src="./../imgs/fb244a2e456a42569d5f8887d5c3ca77.png" alt="image.png"></p>
<p>这个降级也可以删掉了，因为入口就判断了node小于14就退出了。</p>
<p><img src="./../imgs/2aec0f4ee07846c0b7eabf3672691b57.png" alt="image.png"></p>
<p>其他的各种log也可以删减，因为console.log不会换行，代码中可以看到各种</p>
<figure class="highlight arduino"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">console.<span class="built_in">log</span>(...);</span><br><span class="line">console.<span class="built_in">log</span>();</span><br></pre></td></tr></table></figure>

<h3 id="init"><a href="#init" class="headerlink" title="init"></a>init</h3><p>首先是解析用户输入的内容，代码如下： <code>const program = new commander.Command(packageJson.name)....option()....parse(process.argv)</code></p>
<p>注意<code>create-react-app</code>命令并不是commander提供的，commander起到的作用是解析用户输入的内容并给出反馈。</p>
<p>这一步通过用户输入获取几个变量</p>
<ul>
<li><code>projectName</code></li>
<li><code>verbose</code>:print additional logs</li>
<li><code>scriptsVersion</code>:use a non-standard version of react-scripts</li>
<li><code>template</code>:specify a template for the created project</li>
<li><code>usePnp</code></li>
</ul>
<p>另外后续通过 <code>(process.env.npm_config_user_agent || &#39;&#39;).indexOf(&#39;yarn&#39;)</code>获取到了另一个重要变量<code>useYarn</code>;</p>
<p>接下来就是检查cra的版本<code>checkForLatestVersion</code>,</p>
<h4 id="checkForLatestVersion"><a href="#checkForLatestVersion" class="headerlink" title="checkForLatestVersion"></a>checkForLatestVersion</h4><p>获取cra最新版本号可以用两个办法：</p>
<figure class="highlight csharp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">https</span><br><span class="line">     .<span class="keyword">get</span>(</span><br><span class="line">       <span class="string">&#x27;https://registry.npmjs.org/-/package/create-react-app/dist-tags&#x27;</span>,</span><br><span class="line">       ...</span><br></pre></td></tr></table></figure>

<p>出错时使用：</p>
<figure class="highlight scss"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">execSync</span>(&#x27;npm view create-react-app version&#x27;)<span class="selector-class">.toString</span>()<span class="selector-class">.trim</span>();</span><br></pre></td></tr></table></figure>

<p>cra的版本遵循<a href="https://semver.org%2Flang%2Fzh-CN%2F" title="https://semver.org/lang/zh-CN/">semver规范https://semver.org/lang/zh-CN/</a></p>
<p>这里判断是否是最新版本，不是就退出了。因此如果不是用npx来启动脚手架，很可能在这一步就终止了。 <img src="./../imgs/578e5e1e1c6a4f34a9d30ab36f34dcce.png" alt="image.png"></p>
<p>接下来就进入createApp方法了，看名字就知道是个主要方法</p>
<h3 id="createApp"><a href="#createApp" class="headerlink" title="createApp"></a>createApp</h3><p>一上来就是一个很细心的操作：</p>
<figure class="highlight ini"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">const <span class="attr">root</span> = path.resolve(name)<span class="comment">;</span></span><br><span class="line">const <span class="attr">appName</span> = path.basename(root)<span class="comment">;</span></span><br></pre></td></tr></table></figure>

<p>看着有些多余，但其实是考虑了输入的projectName中有路径的情况，比如’&#x2F;aa&#x2F;bb’,’.&#x2F;aa’。</p>
<p>之后就是做一些检查：</p>
<h4 id="checkAppName"><a href="#checkAppName" class="headerlink" title="checkAppName"></a>checkAppName</h4><p>主要是通过<a href="https://www.npmjs.com%2Fpackage%2Fvalidate-npm-package-name" title="https://www.npmjs.com/package/validate-npm-package-name">validate-npm-package-name</a>检查，另外还检查了是否和<code>&#39;react&#39;, &#39;react-dom&#39;, &#39;react-scripts&#39;</code>重名。</p>
<h4 id="isSafeToCreateProjectIn"><a href="#isSafeToCreateProjectIn" class="headerlink" title="isSafeToCreateProjectIn"></a>isSafeToCreateProjectIn</h4><p>待创建项目的目录下是否已经有了一些文件，除这些文件外，其他的文件都会认为可能有覆盖风险而终止命令 <img src="./../imgs/702ab02646dc49fb893867edb9fd0142.png" alt="image.png"></p>
<h4 id="检查yarn、–cwd、pnp支持情况"><a href="#检查yarn、–cwd、pnp支持情况" class="headerlink" title="检查yarn、–cwd、pnp支持情况"></a>检查yarn、–cwd、pnp支持情况</h4><p>检查cwd是来自一个issue：<a href="https://github.com%2Ffacebook%2Fcreate-react-app%2Fissues%2F3326" title="https://github.com/facebook/create-react-app/issues/3326">github.com&#x2F;facebook&#x2F;cr…</a> 之后便是运行run</p>
<h3 id="run"><a href="#run" class="headerlink" title="run"></a>run</h3><p>这个也可以简化一下：</p>
<figure class="highlight scss"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">Promise<span class="selector-class">.all</span>([getInstallPackage(),<span class="built_in">getTemplateInstallPackage</span>()])</span><br><span class="line"><span class="selector-class">.then</span>(()=&gt;<span class="built_in">checkIfOnline</span>())</span><br><span class="line"><span class="selector-class">.then</span>(()=&gt;<span class="built_in">install</span>())</span><br><span class="line"><span class="selector-class">.then</span>(()=&gt;<span class="built_in">executeNodeScript</span>())</span><br></pre></td></tr></table></figure>

<h4 id="确定要安装的包"><a href="#确定要安装的包" class="headerlink" title="确定要安装的包"></a>确定要安装的包</h4><ul>
<li>确定要安装的react-scripts的版本，也支持其他的<code>react-scripts</code></li>
<li>确定template的包及版本，可以是cra-template，也支持其他的<code>template</code></li>
<li>安装的包还包括react、react-dom</li>
</ul>
<h4 id="检查网络"><a href="#检查网络" class="headerlink" title="检查网络"></a>检查网络</h4><p>yarn支持离线安装依赖</p>
<h3 id="install"><a href="#install" class="headerlink" title="install"></a>install</h3><p>确定安装命令和参数，分别有</p>
<p><code>yarnpkg add --exact --offline --enable-pnp --cwd --verbose ...</code></p>
<p><code>npm install --no-audit --save --save-exact --loglevel error --verbose</code></p>
<p>这些参数都是根据上面的流程确定，以<code>spawn(command, args, &#123; stdio: &#39;inherit&#39; &#125;);</code>执行</p>
<h3 id="executeNodeScript"><a href="#executeNodeScript" class="headerlink" title="executeNodeScript"></a>executeNodeScript</h3><p>这一步就是执行了<code>react-scripts</code>包中的init方法，下一节再详细讲</p>
<h3 id="异常处理"><a href="#异常处理" class="headerlink" title="异常处理"></a>异常处理</h3><p>如果运行过程中出错，cra还贴心的将生成的文件删除</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"> <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">&#x27;Aborting installation.&#x27;</span>);</span><br><span class="line"><span class="comment">// On &#x27;exit&#x27; we will delete these files from target directory.</span></span><br><span class="line">...</span><br><span class="line"> <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">`Deleting generated file... <span class="subst">$&#123;chalk.cyan(file)&#125;</span>`</span>);</span><br><span class="line"> fs.<span class="title function_">removeSync</span>(path.<span class="title function_">join</span>(root, file));</span><br><span class="line"> ...</span><br></pre></td></tr></table></figure>

<p>备注：本系列cra版本为4.0.3</p>

        
      </div>

         
    </div>
    
     
  </div>
  
    
<nav id="article-nav">
  <a class="article-nav-btn left "
    
      href="/hexo-blog/2021/11/06/%E5%89%8D%E7%AB%AF%E8%84%9A%E6%89%8B%E6%9E%B6%EF%BC%9A%E4%BB%8E%E5%85%A5%E9%97%A8%E5%88%B0%E8%BF%9B%E9%98%B6%E2%80%94%E2%80%94Create%20React%20App%E6%BA%90%E7%A0%81%E8%A7%A3%E6%9E%90%EF%BC%88%E4%B8%89%EF%BC%89/"
      title="前端脚手架：从入门到进阶——Create React App源码解析（三）"
     >
    <i class="fa-solid fa-angle-left"></i>
    <p class="title-text">
      
        前端脚手架：从入门到进阶——Create React App源码解析（三）
        
    </p>
  </a>
  <a class="article-nav-btn right "
    
      href="/hexo-blog/2021/11/04/%E5%89%8D%E7%AB%AF%E8%84%9A%E6%89%8B%E6%9E%B6%EF%BC%9A%E4%BB%8E%E5%85%A5%E9%97%A8%E5%88%B0%E8%BF%9B%E9%98%B6%E2%80%94%E2%80%94Create%20React%20App%E6%BA%90%E7%A0%81%E8%A7%A3%E6%9E%90%EF%BC%88%E4%B8%80%EF%BC%89/"
      title="前端脚手架：从入门到进阶——Create React App源码解析（一）"
     >

    <p class="title-text">
      
        前端脚手架：从入门到进阶——Create React App源码解析（一）
        
    </p>
    <i class="fa-solid fa-angle-right"></i>
  </a>
</nav>


  
</article>





    </div>
    <div id="footer-wrapper">
      <footer id="footer">
  
  <div id="footer-info" class="inner">
    
    &copy; 2024 Zachary<br>
    <!-- Powered by <a href="https://hexo.io/" target="_blank">Hexo</a> & Theme <a target="_blank" rel="noopener" href="https://github.com/saicaca/hexo-theme-vivia">Vivia</a> -->
      <a target="_blank" rel="noopener" href="https://beian.miit.gov.cn">苏ICP备20030005号-2</a>
  </div>
</footer>

    </div>
    <div class="back-to-top-wrapper">
    <button id="back-to-top-btn" class="back-to-top-btn hide" onclick="topFunction()">
        <i class="fa-solid fa-angle-up"></i>
    </button>
</div>

<script>
    function topFunction() {
        window.scroll({ top: 0, behavior: 'smooth' });
    }
    let btn = document.getElementById('back-to-top-btn');
    function scrollFunction() {
        if (document.body.scrollTop > 600 || document.documentElement.scrollTop > 600) {
            btn.classList.remove('hide')
        } else {
            btn.classList.add('hide')
        }
    }
    window.onscroll = function() {
        scrollFunction();
    }
</script>

  </div>
  <script src="/hexo-blog/js/light-dark-switch.js"></script>
</body>
</html>
